#!/usr/bin/python3
"""
(c) Copyright 2024, Denis Rozhnovskiy <pytelemonbot@mail.ru>
pyTeleMonBot - A simple Telegram bot designed to gather basic information about
the status of your local computers and/or servers from Glances
"""

from telebot import types
from app import (
    bot,
    config,
    jinja,
    TemplateError,
    build_logger,
    exceptions
)
from app.utilities.utilities import (
    round_up,
    split_str,
    replace_symbol,
    format_bytes,
    get_emoji
)
from app.core.keyboards import Keyboard
from app.core.glances import GlancesPoller

# Logger instance
log = build_logger(__name__)

# GlancesPoller one common instance
api_data = GlancesPoller()

# Keyboard one common instance
kb = Keyboard()

ERROR_ACCESS_LOG_TEMPLATE = ("Request from user: {0}, user_id {1}. Ignored. "
                             "Reason: user not allowed (see BotSettings class in app/settings/bot_settings.py)."
                             " [lang: {2}, bot: {3}]")
ERROR_USER_BLOCKED_TEMPLATE = "Sorry, you don't have the rights to access this bot...("
INFO_USER_SESSION_START_TEMPLATE = "user {0}, user_id {1}. Use function {2}"
VALUE_ERR_TEMPLATE = "Invalid message format"
TPL_ERR_TEMPLATE = "Error parsing template"


@bot.callback_query_handler(func=lambda call: call.data == 'history_load')
def history_load(call: types.CallbackQuery):
    """
    Get callback query - history load average
    """
    try:
        if call.message.from_user.id in config.ALLOWED_USER_IDS:
            log.info(
                INFO_USER_SESSION_START_TEMPLATE.format(
                    call.message.from_user.username,
                    call.message.from_user.id,
                    "callback_query_handler['history_load']"
                )
            )
            context: dict | None = api_data.get_metrics('cpu', True, 10)
            i = 0
            chart_context = {}
            for load in (context["user"]):
                date = split_str(load[i], 'T')
                time = split_str(date[1], '.')
                chart_context.update({f'{date[0]}, {time[0]}': load[1]})
            tpl = jinja.get_template('load_average_history.jinja2')
            bot_answer: str = tpl.render(
                thought_balloon=get_emoji('thought_balloon'),
                desktop_computer=get_emoji('desktop_computer'),
                chart_context=chart_context
            )
            bot.edit_message_text(
                chat_id=call.message.chat.id,
                message_id=call.message.message_id,
                text=bot_answer
            )
        else:
            log.error(
                ERROR_ACCESS_LOG_TEMPLATE.format(
                    call.message.from_user.username,
                    call.message.from_user.id,
                    call.message.from_user.language_code,
                    call.message.from_user.is_bot
                )
            )
            bot.edit_message_text(
                chat_id=call.message.chat.id,
                message_id=call.message.message_id,
                text=ERROR_USER_BLOCKED_TEMPLATE
            )
    except ValueError as err:
        raise exceptions.PyTeleMonBotHandlerError(VALUE_ERR_TEMPLATE) from err
    except TemplateError as err_tpl:
        raise exceptions.PyTeleMonBotTemplateError(TPL_ERR_TEMPLATE) from err_tpl


@bot.message_handler(commands=['help', 'start'])
def start(message) -> None:
    """
    The entry point for starting a dialogue with the bot
    """
    try:
        if message.from_user.id in config.ALLOWED_USER_IDS:
            log.info(
                INFO_USER_SESSION_START_TEMPLATE.format(
                    message.from_user.username,
                    message.from_user.id,
                    "start"
                )
            )
            markup = kb.build_reply_keyboard()
            first_name: str = message.from_user.first_name
            tpl = jinja.get_template('index.jinja2')
            bot_answer: str = tpl.render(first_name=first_name)
            bot.send_message(
                message.chat.id,
                text=bot_answer,
                reply_markup=markup
            )
        else:
            log.error(
                ERROR_ACCESS_LOG_TEMPLATE.format(
                    message.from_user.username,
                    message.from_user.id,
                    message.from_user.language_code,
                    message.from_user.is_bot
                )
            )
            bot.send_message(
                message.chat.id,
                ERROR_USER_BLOCKED_TEMPLATE
            )
    except ValueError as err:
        raise exceptions.PyTeleMonBotHandlerError(VALUE_ERR_TEMPLATE) from err
    except TemplateError as err_tpl:
        raise exceptions.PyTeleMonBotTemplateError(TPL_ERR_TEMPLATE) from err_tpl


@bot.message_handler(regexp="Load average")
def get_average(message) -> None:
    """
    Get the average value
    """
    try:
        if message.from_user.id in config.ALLOWED_USER_IDS:
            log.info(INFO_USER_SESSION_START_TEMPLATE.format(message.from_user.username, message.from_user.id,
                                                             "get_average"))
            context: dict | None = api_data.get_metrics('load')
            tpl = jinja.get_template('load_average.jinja2')
            bot_answer: str = tpl.render(
                thought_balloon=get_emoji('thought_balloon'),
                desktop_computer=get_emoji('desktop_computer'),
                min1=round_up(context['min1']),
                min5=round_up(context['min5']),
                min15=round_up(context['min15']),
                battery=get_emoji('battery'),
                cpu_core=context['cpucore']
            )
            inline_button = kb.build_inline_keyboard("History", "history_load")
            bot.send_message(
                message.chat.id,
                text=bot_answer,
                reply_markup=inline_button
            )
        else:
            log.error(
                ERROR_ACCESS_LOG_TEMPLATE.format(
                    message.from_user.username,
                    message.from_user.id,
                    message.from_user.language_code,
                    message.from_user.is_bot
                )
            )
            bot.send_message(
                message.chat.id,
                ERROR_USER_BLOCKED_TEMPLATE
            )
    except ValueError as err:
        raise exceptions.PyTeleMonBotHandlerError(VALUE_ERR_TEMPLATE) from err
    except TemplateError as err_tpl:
        raise exceptions.PyTeleMonBotTemplateError(TPL_ERR_TEMPLATE) from err_tpl


@bot.message_handler(regexp="Memory load")
def get_memory(message) -> None:
    """
    Get memory usage statistics
    """
    try:
        if message.from_user.id in config.ALLOWED_USER_IDS:
            log.info(
                INFO_USER_SESSION_START_TEMPLATE.format(
                    message.from_user.username,
                    message.from_user.id, "get_memory"
                )
            )
            context: dict | None = api_data.get_metrics('mem')
            context_memory: dict = {}
            for key, value in context.items():
                if key != 'percent':
                    context_memory.update({key.title(): format_bytes(value)})
                else:
                    context_memory.update({key.title(): value})
            tpl = jinja.get_template('memory.jinja2')
            bot_answer: str = tpl.render(
                thought_balloon=get_emoji('thought_balloon'),
                abacus=get_emoji('abacus'),
                context=context_memory
            )
            bot.send_message(message.chat.id, text=bot_answer)
        else:
            log.error(
                ERROR_ACCESS_LOG_TEMPLATE.format(
                    message.from_user.username,
                    message.from_user.id,
                    message.from_user.language_code,
                    message.from_user.is_bot
                )
            )
            bot.send_message(
                message.chat.id,
                ERROR_USER_BLOCKED_TEMPLATE
            )
    except ValueError as err:
        raise exceptions.PyTeleMonBotHandlerError(VALUE_ERR_TEMPLATE) from err
    except TemplateError as err_tpl:
        raise exceptions.PyTeleMonBotTemplateError(TPL_ERR_TEMPLATE) from err_tpl


@bot.message_handler(regexp="Sensors")
def get_sensors(message) -> None:
    """
    Get all sensors information
    """
    try:
        if message.from_user.id in config.ALLOWED_USER_IDS:
            log.info(
                INFO_USER_SESSION_START_TEMPLATE.format(
                    message.from_user.username,
                    message.from_user.id,
                    "get_sensors"
                )
            )
            context = api_data.get_metrics('sensors')
            context_sensors = {}
            for data in context:
                context_sensors.update({data['label']: data['value']})
            tpl = jinja.get_template('sensors.jinja2')
            bot_answer = tpl.render(
                thought_balloon=get_emoji('thought_balloon'),
                thermometer=get_emoji('thermometer'),
                exclamation=get_emoji('red_exclamation_mark'),
                melting_face=get_emoji('melting_face'),
                context=context_sensors)
            bot.send_message(message.chat.id, text=bot_answer)
        else:
            log.error(
                ERROR_ACCESS_LOG_TEMPLATE.format(
                    message.from_user.username,
                    message.from_user.id,
                    message.from_user.language_code,
                    message.from_user.is_bot
                )
            )
            bot.send_message(
                message.chat.id,
                ERROR_USER_BLOCKED_TEMPLATE
            )
    except ValueError as err:
        raise exceptions.PyTeleMonBotHandlerError(VALUE_ERR_TEMPLATE) from err
    except TemplateError as err_tpl:
        raise exceptions.PyTeleMonBotTemplateError(TPL_ERR_TEMPLATE) from err_tpl


@bot.message_handler(regexp="Process")
def get_process(message) -> None:
    """
    Get process count
    """
    try:
        if message.from_user.id in config.ALLOWED_USER_IDS:
            log.info(
                INFO_USER_SESSION_START_TEMPLATE.format(
                    message.from_user.username,
                    message.from_user.id,
                    "get_process"
                )
            )
            context = api_data.get_metrics('processcount')
            context_process = {}
            for key, value in context.items():
                context_process.update({key.title(): value})
            tpl = jinja.get_template('process.jinja2')
            bot_answer = tpl.render(
                thought_balloon=get_emoji('thought_balloon'),
                horizontal_traffic_light=get_emoji('horizontal_traffic_light'),
                context=context_process
            )
            bot.send_message(message.chat.id, text=bot_answer)
        else:
            log.error(
                ERROR_ACCESS_LOG_TEMPLATE.format(
                    message.from_user.username,
                    message.from_user.id,
                    message.from_user.language_code,
                    message.from_user.is_bot
                )
            )
            bot.send_message(
                message.chat.id,
                ERROR_USER_BLOCKED_TEMPLATE
            )
    except ValueError as err:
        raise exceptions.PyTeleMonBotHandlerError(VALUE_ERR_TEMPLATE) from err
    except TemplateError as err_tpl:
        raise exceptions.PyTeleMonBotTemplateError(TPL_ERR_TEMPLATE) from err_tpl


@bot.message_handler(regexp="Uptime")
def get_uptime(message) -> None:
    """
    Get uptime info
    """
    try:
        if message.from_user.id in config.ALLOWED_USER_IDS:
            log.info(
                INFO_USER_SESSION_START_TEMPLATE.format(
                    message.from_user.username,
                    message.from_user.id,
                    "get_uptime"
                )
            )
            context = api_data.get_metrics('uptime')
            tpl = jinja.get_template('uptime.jinja2')
            bot_answer = tpl.render(
                thought_balloon=get_emoji('thought_balloon'),
                hourglass_not_done=get_emoji('hourglass_not_done'),
                context=context
            )
            bot.send_message(message.chat.id, text=bot_answer)
        else:
            log.error(
                ERROR_ACCESS_LOG_TEMPLATE.format(
                    message.from_user.username,
                    message.from_user.id,
                    message.from_user.language_code,
                    message.from_user.is_bot
                )
            )
            bot.send_message(
                message.chat.id,
                ERROR_USER_BLOCKED_TEMPLATE
            )
    except ValueError as err:
        raise exceptions.PyTeleMonBotHandlerError(VALUE_ERR_TEMPLATE) from err
    except TemplateError as err_tpl:
        raise exceptions.PyTeleMonBotTemplateError(TPL_ERR_TEMPLATE) from err_tpl


@bot.message_handler(regexp="File system")
def get_fs(message) -> None:
    """
    Get file system info
    """
    try:
        if message.from_user.id in config.ALLOWED_USER_IDS:
            log.info(
                INFO_USER_SESSION_START_TEMPLATE.format(
                    message.from_user.username,
                    message.from_user.id,
                    "get_fs"
                )
            )
            context = api_data.get_metrics('fs')
            context_process = []
            for disk in context:
                context_process += {
                    "device_name": disk["device_name"],
                    "used": format_bytes(disk["used"]),
                    "percent": disk["percent"],
                    "free": format_bytes(disk["free"]),
                },
            tpl = jinja.get_template('fs.jinja2')
            bot_answer = tpl.render(
                thought_balloon=get_emoji('thought_balloon'),
                floppy_disk=get_emoji('floppy_disk'),
                minus=get_emoji('minus'),
                context=context_process
            )
            bot.send_message(message.chat.id, text=bot_answer)
        else:
            log.error(
                ERROR_ACCESS_LOG_TEMPLATE.format(
                    message.from_user.username,
                    message.from_user.id,
                    message.from_user.language_code,
                    message.from_user.is_bot
                )
            )
            bot.send_message(
                message.chat.id,
                ERROR_USER_BLOCKED_TEMPLATE
            )
    except ValueError as err:
        raise exceptions.PyTeleMonBotHandlerError(VALUE_ERR_TEMPLATE) from err
    except TemplateError as err_tpl:
        raise exceptions.PyTeleMonBotTemplateError(TPL_ERR_TEMPLATE) from err_tpl


@bot.message_handler(regexp="Containers")
def get_containers(message) -> None:
    """
    Get docker containers info
    """
    try:
        if message.from_user.id in config.ALLOWED_USER_IDS:
            log.info(
                INFO_USER_SESSION_START_TEMPLATE.format(
                    message.from_user.username,
                    message.from_user.id,
                    "get_containers"
                )
            )
            context = api_data.get_metrics('containers')
            if context == {}:
                tpl = jinja.get_template('none.jinja2')
                bot_answer = tpl.render(thought_balloon=get_emoji('thought_balloon'))
                bot.send_message(message.chat.id, text=bot_answer)
            else:
                context_process = []
                for value in context['containers']:
                    created_date = split_str(value['Created'], 'T')
                    created_time = split_str(created_date[1], '.')
                    image_name = replace_symbol(value['Image'])
                    context_process += {
                        'name': value['name'].title(),
                        'Uptime': value['Uptime'],
                        'Created': f"{created_date[0]}, {created_time[0]}",
                        'Image': image_name[0],
                        'Status': value['Status']},
                tpl = jinja.get_template('containers.jinja2')
                bot_answer = tpl.render(
                    thought_balloon=get_emoji('thought_balloon'),
                    luggage=get_emoji('pushpin'), minus=get_emoji('minus'),
                    context=context_process
                )
                bot.send_message(message.chat.id, text=bot_answer)
        else:
            log.error(
                ERROR_ACCESS_LOG_TEMPLATE.format(
                    message.from_user.username,
                    message.from_user.id,
                    message.from_user.language_code,
                    message.from_user.is_bot
                )
            )
            bot.send_message(
                message.chat.id,
                ERROR_USER_BLOCKED_TEMPLATE
            )
    except ValueError as err:
        raise exceptions.PyTeleMonBotHandlerError(VALUE_ERR_TEMPLATE) from err
    except TemplateError as err_tpl:
        raise exceptions.PyTeleMonBotTemplateError(TPL_ERR_TEMPLATE) from err_tpl


@bot.message_handler(content_types=['text'])
def func(message) -> None:
    """
    Echo bot functionality for answering questions that are not described in the code
    """
    try:
        if message.text == f"{get_emoji('waving_hand')} Say hello!":
            log.info(
                INFO_USER_SESSION_START_TEMPLATE.format(
                    message.from_user.username,
                    message.from_user.id,
                    "say_hellow"
                )
            )
            bot.send_message(
                message.chat.id,
                text=f"{get_emoji('waving_hand')} "
                     f"Hello there! Thanks for saying hello! This is such a rarity nowadays!"
                     f" {get_emoji('partying_face')}"
            )
        else:
            log.error(
                ERROR_ACCESS_LOG_TEMPLATE.format(
                    message.from_user.username,
                    message.from_user.id,
                    message.from_user.language_code,
                    message.from_user.is_bot
                )
            )
            bot.reply_to(message, f"{get_emoji('face_with_monocle')} " + message.text)
    except ValueError as err:
        raise exceptions.PyTeleMonBotHandlerError(VALUE_ERR_TEMPLATE) from err
    except TemplateError as err_tpl:
        raise exceptions.PyTeleMonBotTemplateError(TPL_ERR_TEMPLATE) from err_tpl
